package file_cleaner

import (
	"errors"
	"file-cleaner/internal/logger"
	"file-cleaner/internal/pkg/file_cleaner/clean_handler"
	"file-cleaner/internal/pkg/file_cleaner/cleaner"
	"file-cleaner/internal/pkg/file_cleaner/pool"
	"file-cleaner/internal/pkg/file_cleaner/querier"
	"file-cleaner/internal/pkg/file_cleaner/query_handler"
	"file-cleaner/internal/pkg/file_cleaner/queue"
	"sync"
)

// TODO: 1 统计信息(clean_handler)
// TODO: 2 处理过的文件，再次遍历到跳过(query handler)

type FileCleanerApi interface {
	Query(dirPath string, cascade bool) error
	Run() error
	Done()
}

type fileCleanerApiImpl struct {
	filepathQueue, queryQueue, cleanTaskInfoQueue queue.Queue
	queryPool, cleanPool                          *pool.Pool
	fileQuerier                                   *querier.FileQuerier
	queryHandlerChain                             *query_handler.QueryHandlerChain
	cleanHandlerChain                             *clean_handler.CleanHandlerChain
	fileCleaner                                   *cleaner.FileCleaner
	wg                                            *sync.WaitGroup
	isRunning                                     bool
}

func newFileCleanerApi(
	filepathQueue, queryQueue, cleanTaskInfoQueue queue.Queue,
	queryPool, cleanPool *pool.Pool,
	fileQuerier *querier.FileQuerier,
	queryHandlerChain *query_handler.QueryHandlerChain,
	cleanHandlerChain *clean_handler.CleanHandlerChain,
	fileCleaner *cleaner.FileCleaner,
) FileCleanerApi {
	api := fileCleanerApiImpl{
		filepathQueue:      filepathQueue,
		queryQueue:         queryQueue,
		cleanTaskInfoQueue: cleanTaskInfoQueue,
		queryPool:          queryPool,
		cleanPool:          cleanPool,
		fileQuerier:        fileQuerier,
		queryHandlerChain:  queryHandlerChain,
		cleanHandlerChain:  cleanHandlerChain,
		fileCleaner:        fileCleaner,
		wg:                 new(sync.WaitGroup),
		isRunning:          false,
	}
	return FileCleanerApi(&api)
}

func NewFileCleanerApi(
	poolCap int,
	queryHandlers []query_handler.QueryHandler,
	beforeCleanHandlers, mainCleanHandlers, troubleshootingCleanHandlers, finishCleanHandlers []clean_handler.CleanHandler,
) (FileCleanerApi, error) {
	poolCap = optimisePoolCap(poolCap)

	qpool, err := pool.NewPool(poolCap)
	if err != nil {
		return nil, err
	}
	cpool, err := pool.NewPool(poolCap)
	if err != nil {
		return nil, err
	}
	fq, qq, cq := queue.NewQueue(), queue.NewQueue(), queue.NewQueue()

	qhc := query_handler.NewQueryHandlerChain(fq, qq, qpool)
	chc := clean_handler.NewCleanHandlerChain(cq, cpool)

	if queryHandlers != nil && len(queryHandlers) > 0 {
		err = qhc.RegisterHandler(queryHandlers...)
		if err != nil {
			return nil, err
		}
	}

	if beforeCleanHandlers != nil && len(beforeCleanHandlers) > 0 {
		err = chc.RegisterBeforeHandler(beforeCleanHandlers...)
		if err != nil {
			return nil, err
		}
	}
	if mainCleanHandlers != nil && len(mainCleanHandlers) > 0 {
		err = chc.RegisterMainHandler(mainCleanHandlers...)
		if err != nil {
			return nil, err
		}
	}
	if troubleshootingCleanHandlers != nil && len(troubleshootingCleanHandlers) > 0 {
		err = chc.RegisterTroubleshootingHandler(troubleshootingCleanHandlers...)
		if err != nil {
			return nil, err
		}
	}
	if finishCleanHandlers != nil && len(finishCleanHandlers) > 0 {
		err = chc.RegisterFinishHandler(finishCleanHandlers...)
		if err != nil {
			return nil, err
		}
	}

	return newFileCleanerApi(fq, qq, cq, qpool, cpool, querier.NewFileQuerier(poolCap, fq), qhc, chc, cleaner.NewFileCleaner(qq, cq)), nil
}

func (f *fileCleanerApiImpl) Query(dirPath string, cascade bool) error {
	return f.fileQuerier.Query(dirPath, cascade)
}

func (f *fileCleanerApiImpl) Run() error {
	if f.isRunning {
		return errors.New("cleaner is running")
	}
	f.isRunning = true
	defer func() { f.isRunning = false }()

	f.wg.Add(3)
	go func() {
		f.queryHandlerChain.Handle()
		f.wg.Done()
	}()

	go func() {
		f.cleanHandlerChain.Handle()
		f.wg.Done()
	}()

	go func() {
		f.fileCleaner.Run()
		f.wg.Done()
	}()

	return nil
}

func (f *fileCleanerApiImpl) Done() {
	f.fileQuerier.Done()
	f.wg.Wait()
	f.queryPool.Wait()
	f.cleanPool.Wait()
	logger.Info("Clean task complete")
}
